﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using MonoStrategy.RenderSystem;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace MonoStrategy
{
    [ObfuscationAttribute(Feature = "renaming", ApplyToMembers = true)]
    public class ListBoxItem
    {
        public Object[] Params { get; private set; }
        public Object Callback { get; set; }

        public ListBoxItem(params String[] inParams)
        {
            Params = new Object[inParams.Length];

            for (int i = 0; i < Params.Length; i++)
            {
                if (inParams == null)
                    throw new ArgumentNullException();

                Params[i] = inParams[i];
            }
        }
    }

    [ObfuscationAttribute(Feature = "renaming", ApplyToMembers = true)]
    public class ListBox : Control
    {
        private Int32 m_ItemWidth, m_ItemHeight;
        private readonly List<ListBoxItem> m_Items = new List<ListBoxItem>();
        private int m_SelectionIndex = -1;

        [XmlAttribute("ItemTemplate")]
        public String ItemTemplateString { get; set; }
        [XmlIgnore]
        public Template ItemTemplate { get; set; }
        [XmlIgnore]
        public int Count { get { return Children.Count; } }
        [XmlIgnore]
        public int SelectionIndex
        {
            get
            {
                return m_SelectionIndex;
            }
            set
            {
                if (value == m_SelectionIndex)
                    return;

                foreach (var child in Children)
                {
                    var item = (Button)child;

                    item.IsDown = false;
                }

                if (value >= 0)
                {
                    if (value >= Children.Count)
                        throw new ArgumentOutOfRangeException();

                    m_SelectionIndex = value;
                    ((Button)Children.ElementAt(value)).IsDown = true;
                }
                else
                    m_SelectionIndex = -1;
            }
        }

        public event DNotifyHandler<ListBox, ListBoxItem, ListBoxItem> OnSelectionChanged;

        public ListBox()
            : base()
        {
        }

        public override void XMLPostProcess(XMLGUILayout inLayout)
        {
            base.XMLPostProcess(inLayout);

            if (String.IsNullOrEmpty(ItemTemplateString))
                throw new ArgumentException("A ListBox must specify an item template.");

            ItemTemplate = inLayout.GetTemplate(ItemTemplateString);

            ItemTemplate.XMLPostProcess(inLayout);

            if (Width <= 0)
                throw new ArgumentException("A ListBox must have a positive, fixed width.");

            Control ctrl = CreateItem(null);

            if (((m_ItemWidth = ctrl.Width) <= 0) || ((m_ItemHeight = ctrl.Height) <= 0))
                throw new ArgumentException("A ListBox item must have a positive, fixed width and height.");

            if (!(ctrl is Button))
                throw new ArgumentException("A ListBoxItem shall be a subclass of \"Button\".");

            if (m_ItemWidth > Width)
                throw new ArgumentException("A ListBox must be at least large enough to hold one entry.");
        }

        private Control CreateItem(ListBoxItem inNewItem)
        {
            List<Control> childs, presenters;
            int iLeft = Children.Count;

            if(inNewItem != null)
                ItemTemplate.Instantiate(out childs, out presenters, inNewItem.Params);
            else
                ItemTemplate.Instantiate(out childs, out presenters);

            if(childs.Count != 1)
                throw new ArgumentException("A ListBox item template must have exactly one control on its first level.");

            return childs.First();
        }

        public void AddItem(ListBoxItem inNewItem)
        {
            Button item = (Button)CreateItem(inNewItem);

            int iLeft = Width / m_ItemWidth;
            int iTop = Children.Count / iLeft;

            item.Left = m_ItemWidth * (Children.Count % iLeft);
            item.Top = m_ItemHeight * iTop;

            item.OnClick += OnItemClick;

            Children.Add(item);
            m_Items.Add(inNewItem);
        }

        void OnItemClick(Button inSender)
        {
            int backup = SelectionIndex;
            m_SelectionIndex = Children.IndexOf(inSender);

            if (OnSelectionChanged != null)
            {
                OnSelectionChanged(
                    this,
                    (backup >= 0) ? m_Items[backup] : null,
                    (SelectionIndex >= 0) ? m_Items[SelectionIndex] : null);
            }
        }

        public void RemoveAt(int inIndex)
        {
            Children.Remove(Children.ElementAt(inIndex));
            m_Items.RemoveAt(inIndex);
        }

        public void Clear()
        {
            Children.Clear();
            m_Items.Clear();
        }
    }
}
